﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="TransactionsViewModel.cs" company="Concept Architecture">
//   (c) 2010 Muhammad Umer Farooq
// </copyright>
// <summary>
//   Defines the TransactionsViewModel type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Hisaab.Windows.Client.ViewModel
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Diagnostics.Contracts;
    using System.Linq;
    using System.Windows.Forms;
    using Business.Interfaces;
    using Common;
    using Documents;
    using Hisaab.Common;

    using Microsoft.Practices.ServiceLocation;

    using Model;

    /// <summary>
    /// Defines the <seealso cref="TransactionsViewModel"/> type.
    /// </summary>
    public class TransactionsViewModel : ViewModelBase
    {
        /// <summary>
        /// The Save Command
        /// </summary>
        public static readonly ActiveAwareCommand EditCommand = new ActiveAwareCommand();

        /// <summary>
        /// The Relate Transaction Command
        /// </summary>
        public static readonly ActiveAwareCommand RelateTransactionCommand = new ActiveAwareCommand();

        /// <summary>
        /// The Delete Command
        /// </summary>
        public static readonly ActiveAwareCommand DeleteCommand = new ActiveAwareCommand();

        /// <summary>
        /// The refresh command
        /// </summary>
        public static readonly ActiveAwareCommand RefreshCommand = new ActiveAwareCommand();

        /// <summary>
        /// The filter by account command
        /// </summary>
        public static readonly ActiveAwareCommand FilterAccountCommand = new ActiveAwareCommand();

        /// <summary>
        /// The sort Command
        /// </summary>
        public static readonly ActiveAwareCommand SortCommand = new ActiveAwareCommand();

        /// <summary>
        /// Order by command
        /// </summary>
        public static readonly ActiveAwareCommand OrderByCommand = new ActiveAwareCommand();

        /// <summary>
        /// By Month Command
        /// </summary>
        public static readonly ActiveAwareCommand ByMonthCommand = new ActiveAwareCommand();

        /// <summary>
        /// Account object
        /// </summary>
        private readonly ITransactionManager _transactionManager;

        /// <summary>
        /// Selected transaction
        /// </summary>
        private Transaction _selectedTransaction;

        /// <summary>
        /// Backing field for Selected view model
        /// </summary>
        private TransactionViewModel _selectedTransactionViewModel;

        /// <summary>
        /// Transactions list
        /// </summary>
        private ObservableCollection<Transaction> _transactions;

        /// <summary>
        /// Transactions list
        /// </summary>
        private ObservableCollection<KeyValuePair<int, decimal>> _expanceChartData;

        /// <summary>
        /// Transactions list
        /// </summary>
        private ObservableCollection<KeyValuePair<TransactionTypes, decimal>> _comparisonChartData;

        /// <summary>
        /// Order records by flag
        /// </summary>
        private OrderByType _orderBy = OrderByType.Ascending;

        /// <summary>
        /// Filter by flag
        /// </summary>
        private SortByType _sortBy = SortByType.Date;

        /// <summary>
        /// Filter by account id
        /// </summary>
        private int _filterAccountId;

        /// <summary>
        /// Filter by month
        /// </summary>
        private int _filterMonth;

        /// <summary>
        /// Initializes a new instance of the <see cref="TransactionsViewModel"/> class.
        /// </summary>
        public TransactionsViewModel()
            : this(ServiceLocator.Current.GetInstance<ITransactionManager>())
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="TransactionsViewModel"/> class.
        /// </summary>
        /// <param name="transactionManager">The transaction manager.</param>
        public TransactionsViewModel(ITransactionManager transactionManager)
        {
            Contract.Requires<ArgumentNullException>(transactionManager != null);

            _transactionManager = transactionManager;
        }

        /// <summary>
        /// Gets the transactions.
        /// </summary>
        public ObservableCollection<Transaction> Transactions
        {
            get
            {
                if (_transactions == null)
                {
                    RefreshList(null);
                }

                return _transactions;
            }

            private set
            {
                if (value != _transactions)
                {
                    _transactions = value;

                    OnPropertyChanged("Transactions");
                }
            }
        }

        /// <summary>
        /// Gets the transactions.
        /// </summary>
        public ObservableCollection<KeyValuePair<int, decimal>> ExpanseChartData
        {
            get
            {
                if (_expanceChartData == null)
                {
                    RefreshList(null);
                }

                return _expanceChartData;
            }

            private set
            {
                if (value != _expanceChartData)
                {
                    _expanceChartData = value;

                    OnPropertyChanged("ExpanseChartData");
                }
            }
        }

        /// <summary>
        /// Gets the transactions.
        /// </summary>
        public ObservableCollection<KeyValuePair<TransactionTypes, decimal>> ComparisonChartData
        {
            get
            {
                if (_comparisonChartData == null)
                {
                    RefreshList(null);
                }

                return _comparisonChartData;
            }

            private set
            {
                if (value != _comparisonChartData)
                {
                    _comparisonChartData = value;

                    OnPropertyChanged("ComparisonChartData");
                }
            }
        }

        /// <summary>
        /// Gets or sets the selected transaction.
        /// </summary>
        /// <value>
        /// The selected transaction.
        /// </value>
        public Transaction SelectedTransaction
        {
            get
            {
                return _selectedTransaction;
            }

            set
            {
                if (value != _selectedTransaction)
                {
                    _selectedTransaction = value;
                    OnPropertyChanged("SelectedTransaction");
                    SelectedTransactionViewModel = new TransactionViewModel(value);
                }
            }
        }

        /// <summary>
        /// Gets or sets the selected transaction view model.
        /// </summary>
        /// <value>
        /// The selected transaction view model.
        /// </value>
        public TransactionViewModel SelectedTransactionViewModel
        {
            get
            {
                return _selectedTransactionViewModel;
            }

            set
            {
                _selectedTransactionViewModel = value;
                OnPropertyChanged("SelectedTransactionViewModel");
            }
        }

        /// <summary>
        /// Refreshes the list.
        /// </summary>
        /// <param name="obj">The object</param>
        public void RefreshList(object obj)
        {
            var transactionFilter = new Business.Entities.TransactionFilter
                                        {
                                            SortBy = (Business.Entities.SortByType)_sortBy,
                                            OrderBy = (Business.Entities.OrderByType)_orderBy,
                                            StartDate = GetFilterStartDate(_filterMonth),
                                            EndDate = DateTime.Today
                                        };

            var data = _transactionManager.FilteredList(transactionFilter, _filterAccountId)
                .Select(x => x.Map<Business.Entities.Transaction, Transaction>());
            Transactions = new ObservableCollection<Transaction>(data.ToList());

            var expanseChartdata = new Dictionary<int, decimal>();

            foreach (var transaction in data)
            {
                foreach (var tag in transaction.Tags)
                {
                    if (expanseChartdata.ContainsKey(tag.TagId))
                    {
                        expanseChartdata[tag.TagId] += transaction.Amount;
                    }
                    else
                    {
                        expanseChartdata.Add(tag.TagId, transaction.Amount);
                    }
                }
            }

            var comparisonChartdata = Transactions.GroupBy(t => t.TransactionType).Select(g => new KeyValuePair<TransactionTypes, decimal>(g.Key, g.Sum(s => s.Amount)));
            ExpanseChartData = new ObservableCollection<KeyValuePair<int, decimal>>(expanseChartdata);
            ComparisonChartData = new ObservableCollection<KeyValuePair<TransactionTypes, decimal>>(comparisonChartdata);
        }

        /// <summary>
        /// Edits the transaction.
        /// </summary>
        /// <param name="obj">The object</param>
        public void EditTransaction(object obj)
        {
            var viewModel = new TransactionEditorViewModel(SelectedTransaction);
            var transactionEditorDocument = new TransactionEditorDocument(viewModel);

            DocumentManager.Instance.ActiveDocument = transactionEditorDocument;
        }

        /// <summary>
        /// Deletes the transaction.
        /// </summary>
        /// <param name="obj">The object</param>
        public void DeleteTransaction(object obj)
        {
            if (ServiceLocator.Current.GetInstance<IWindowDialogService>().ShowMessage(
                   "Are you sure you want to delete selected vehicle?",
                   "Hissab",
                   MessageBoxButtons.YesNo,
                   MessageBoxIcon.Question)
                   == DialogResult.Yes)
            {
                _transactionManager.Delete(SelectedTransaction.Map<Transaction, Business.Entities.Transaction>());
                Transactions.Remove(SelectedTransaction);
            }
        }

        /// <summary>
        /// Determines whether this instance [can edit transcation].
        /// </summary>
        /// <param name="obj">The object</param>
        /// <returns>
        ///   <c>true</c> if this instance [can edit transaction]; otherwise, <c>false</c>.
        /// </returns>
        public bool CanEditTransaction(object obj)
        {
            return SelectedTransaction != null;
        }

        /// <summary>
        /// Determines whether this instance [can delete transaction] the specified obj.
        /// </summary>
        /// <param name="obj">The object</param>
        /// <returns>
        ///   <c>true</c> if this instance [can delete transaction] the specified obj; otherwise, <c>false</c>.
        /// </returns>
        public bool CanDeleteTransaction(object obj)
        {
            return SelectedTransaction != null;
        }

        /// <summary>
        /// Filters the by account.
        /// </summary>
        /// <param name="accountId">The sender object </param>
        public void FilterByAccount(object accountId)
        {
            _filterAccountId = int.Parse(accountId.ToString());
            RefreshList(null);
        }

        /// <summary>
        /// Sorts the specified obj.
        /// </summary>
        /// <param name="obj">The type of sorting.</param>
        public void Sort(object obj)
        {
            Enum.TryParse(obj.ToString(), true, out _sortBy);
            RefreshList(null);
        }

        /// <summary>
        /// Orders the by.
        /// </summary>
        /// <param name="obj">The Order sequence</param>
        public void OrderBy(object obj)
        {
            Enum.TryParse(obj.ToString(), true, out _orderBy);
            RefreshList(null);
        }

        /// <summary>
        /// Filters the by month.
        /// </summary>
        /// <param name="months">The filter amount</param>
        public void FilterByMonth(object months)
        {
            _filterMonth = int.Parse(months.ToString());
            RefreshList(null);
        }

        /// <summary>
        /// Determines whether this instance [can relate transaction] the specified obj.
        /// </summary>
        /// <param name="obj">The event object</param>
        /// <returns>
        /// <c>true</c> if this instance [can relate transaction] the specified obj; otherwise, <c>false</c>.
        /// </returns>
        public bool CanRelateTransaction(object obj)
        {
            return SelectedTransaction != null;
        }

        /// <summary>
        /// Relates the transaction.
        /// </summary>
        /// <param name="obj">The event object</param>
        public void RelateTransaction(object obj)
        {
            ServiceLocator.Current.GetInstance<IWindowDialogService>()
                .ShowDialog("Relate Transactions", new RelateTransactionViewModel(SelectedTransaction));
        }

        /// <summary>
        /// Gets the filter start date.
        /// </summary>
        /// <param name="filterMonth">The filter month.</param>
        /// <returns>Date Time to which filter date applied</returns>
        private DateTime GetFilterStartDate(int filterMonth)
        {
            if (filterMonth == 0)
            {
                return DateTime.Today.AddDays(-DateTime.Today.Day + 1);
            }

            if (filterMonth < 0)
            {
                return DateTime.MinValue;
            }

            return DateTime.Today.AddMonths(-filterMonth);
        }
    }
}